package thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程协调
 *
 * synchronized + while{wait()} 一个保证线程唯一 一个保证条件持久 一个保证释放锁
 * 锁住的线程间的协调 [虚假唤醒!]
 *
 * synchronized.让线程间只有一个 wait.则是处理当前的一个线程
 * 这种原生自带的 貌似只能完成简单-绝对的相互逻辑,唤醒手段单一
 * wait:【会偶然激发】的主动挂起(比使用纯while无限轮询强不少)-底层问题:线程挂起后的恢复条件并不完全可控
 * 会释放对象上的锁
 *
 * @author yuank
 * @date 2024/12/24 18:57:25
 */
class SynRun {
    public static void main(String[] args) {
        SynchronizedProduceConsume a = new SynchronizedProduceConsume();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                a.produce();
            }
        }, "AAA1").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                a.produce();
            }
        }, "BBB1").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                a.consume();
            }
        }, "AAA2").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                a.consume();
            }
        }, "BBB2").start();
    }
}

/**
 *
 * synchronized的意义不论什么情况下 只能先获得本对象的唯一标识才可以继续进行
 * wait() 释放锁 并且当前线程阻塞
 * notifyAll() 基于对象唯一唤醒所有相关线程
 *
 */
class SynchronizedProduceConsume {
    private int number = 0;

        /**======**/
    public synchronized void produce() {
        while (number != 0) { /**虚假唤醒问题**/
            try {
                wait();  /**放锁,唤醒也要去取对象锁;synchronized整个块内生效**/
                //Thread.sleep(1000); 让代码块不单线原子化 使得同步有意义
            } catch (InterruptedException e) { throw new RuntimeException(e); }
        }
        /**======**/
        number++;
        System.out.println(Thread.currentThread().getName() + " " + number);
        notifyAll();

    }
        /**======**/
    public synchronized void consume() {
        while (number == 0) { /**虚假唤醒问题**/
            try {
                wait();  /**放锁,唤醒也要去取对象锁;synchronized整个块内生效**/
                //Thread.sleep(1000); 让代码块不单线原子化 使得同步有意义
            } catch (InterruptedException e) { throw new RuntimeException(e); }
        }
        /**======**/
        number--;
        System.out.println(Thread.currentThread().getName() + " " + number);
        notifyAll();
    }
}

/**
 * 多个线程处理唯一资源 协调转变为单线程模式处理
 *
 *   -->
 *   -->A
 *   -->           A
 *                 ↓
 *   -->           B
 *   -->B          ↓
 *   -->           C
 *
 *   -->
 *   -->C
 *   -->
 *
 * 随机ABC的线程堆拿锁 不合条件 await释放锁并且挂起 然后 再ABC块的线程随机一个拿取
 * 如果挂起后又是A的线程拿 会存到A中对应的Condition对象的阻塞队列中 被激活处理的顺序按照队列(先进先出)
 *
 *   Condition所属阻塞队列:
 *          +-------+      +-------+       +-------+
 *   <=head |Thread1| <----|Thread2| <---- |Thread3|  tail<=-
 *          +-------+      +-------+       +-------+
 *
 *
 * **/
class JUCRun{
    public static void main(String[] args) {
        JUCProduceConsume    a = new JUCProduceConsume();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    a.product1();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "AAA1").start();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    a.product2();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "AAA2").start();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    a.consume3();
                } catch (InterruptedException e) { throw new RuntimeException(e); }
            }
        }, "BBB1").start();

    }
}
class JUCProduceConsume{
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void product1() throws InterruptedException {
        lock.lock();

        while (number != 0) { condition1.await();} // 虚假唤醒 + 唤醒后重新getLock

        number++;
        System.out.println(Thread.currentThread().getName() + " " + number);

        condition2.signal();

        lock.unlock();
    }

    public void product2() throws InterruptedException {
        lock.lock();

        while (number != 1) { condition2.await();}

        number++;
        System.out.println(Thread.currentThread().getName() + " " + number);

        condition3.signal();

        lock.unlock();
    }

    public void consume3() throws InterruptedException {
        lock.lock();

        while (number != 2) { condition3.await();}

        number = number - 2;
        System.out.println(Thread.currentThread().getName() + " " + number);

        condition1.signal();

        lock.unlock();
    }
}

/**
 * 一个方法单独持有一个锁: 1方法while里就会释放,无效化
 *                     2涉及到一个唯一资源本质是需要单一线程原子化操作的 所以锁是加在涉及了目标的地方
 * 思路应该改过来 根据number状态处理Thread进行 => 锁住Thread单一进行来维护number
 * **/
class JUCProduceConsumeError {
    private int number = 0;
    private Lock lock1 = new ReentrantLock();
    private Lock lock2 = new ReentrantLock();
    private Lock lock3 = new ReentrantLock();
    private Lock lock4 = new ReentrantLock();
    private Condition condition1 = lock1.newCondition();
    private Condition condition2 = lock2.newCondition();
    private Condition condition3 = lock3.newCondition();
    private Condition condition4 = lock4.newCondition();

    public void product1(){
        lock1.lock();
        while (number != 0) {
            try {
                condition1.await();
            } catch (InterruptedException e) { throw new RuntimeException(e); }
        }
        number++;
        condition1.signal();
        System.out.println(Thread.currentThread().getName() + " " + number);
        lock1.unlock();
    }

    public void product2(){
        lock2.lock();
        while (number != 1) {
            try {
                condition2.await();
            } catch (InterruptedException e) { throw new RuntimeException(e); }
        }
        number++;
        condition2.signal();
        System.out.println(Thread.currentThread().getName() + " " + number);
        lock2.unlock();
    }

    public void product3(){
        lock3.lock();
        while (number != 2) {
            try {
                condition3.await();
            } catch (InterruptedException e) { throw new RuntimeException(e); }
        }
        number++;
        condition3.signal();
        System.out.println(Thread.currentThread().getName() + " " + number);
        lock3.unlock();
    }

    public void consume4(){
        lock4.lock();
        while (number != 3) {
            try {
                condition4.await();
            } catch (InterruptedException e) { throw new RuntimeException(e); }
        }
        number = number -3;
        condition4.signal();
        System.out.println(Thread.currentThread().getName() + " " + number);
        lock4.unlock();
    }
}

